-module(ham_server_app).

-define(PRINT_MSG(Msg), io:format(Msg)).

-define(PRINT(Format, Args), io:format(Format, Args)).

-behaviour(application).

%% Application callbacks
-export([start/0,start/2, stop/1]).

-export([
        handle_apns_error/2,
        handle_apns_delete_subscription/1
        ]).

-record(apns_connection, {apple_host        = "gateway.sandbox.push.apple.com"      :: string(),
                          apple_port        = 2195                                  :: integer(),
                          cert              = undefined                             :: undefined | binary(),
                          cert_file         = "priv/cert.pem"                       :: string(),
                          key               = undefined                             :: undefined | {'RSAPrivateKey'|
                                                                                        'DSAPrivateKey' |
                                                                                        'ECPrivateKey' |
                                                                                        'PrivateKeyInfo', binary()},
                          key_file          = undefined                             :: undefined | string(),
                          cert_password     = undefined                             :: undefined | string(),
                          timeout           = 30000                                 :: integer(),
                          error_fun         = fun(X,Y) -> erlang:display({X,Y}) end :: fun((binary(), apns:status()) -> stop | _),
                          feedback_host     = "feedback.sandbox.push.apple.com"     :: string(),
                          feedback_port     = 2196                                  :: integer(),
                          feedback_fun      = fun erlang:display/1                  :: fun(({calendar:datetime(), string()}) -> _),
                          feedback_timeout  = 30*60*1000                            :: pos_integer(),
                          expires_conn      = 300                                   :: pos_integer(),
                          extra_ssl_opts    = []                                    :: [ssl:ssloption()],
                          error_logger_fun  = fun error_logger:error_msg/2          :: fun((string(), list()) -> _),
                          info_logger_fun   = fun error_logger:info_msg/2           :: fun((string(), list()) -> _)
                          }).


%%%=============================================================================
%%% Application callbacks
%%%=============================================================================

%%------------------------------------------------------------------------------
%% @private
%% @doc
%% This function is called whenever an application is started using
%% application:start/[1,2], and should start the processes of the
%% application. If the application is structured according to the OTP
%% design principles as a supervision tree, this means starting the
%% top supervisor of the tree.
%%
%% @end
%%------------------------------------------------------------------------------
start() ->
    start([], []).

-spec start(StartType, StartArgs) -> {ok, pid()} | {ok, pid(), State} | {error, Reason} when 
    StartType :: normal | {takeover, node()} | {failover, node()},
    StartArgs :: term(),
    State     :: term(),
    Reason    :: term().
start(_StartType, _StartArgs) ->
    print_banner(),
    {ok, Sup} = ham_server_sup:start_link(),
    start_servers(Sup),
    {ok, Listeners} = application:get_env(listen),
    ham_server:open(Listeners),
    print_vsn(),
    {ok, Sup}.

handle_apns_error( A, B) ->
    lager:error("handle apns error. ~p ~p ~n", [A,B]).

handle_apns_delete_subscription( A ) ->
    lager:error("handle apns delete subscription. ~p ~n", [ A ]).

start_servers(Sup) ->
    {ok, EmysqlOpts} = application:get_env(ham_server,emysql),
    lists:foreach(
        fun({Name, F}) when is_function(F) ->
                ?PRINT("~s is starting...", [Name]),
                F(),
                ?PRINT_MSG("[done]~n");
            ({Name, Server}) ->
                ?PRINT("~s is starting...", [Name]),
                start_child(Sup, Server),
                ?PRINT_MSG("[done]~n");
            ({Name, Server, Opts}) ->
                ?PRINT("~s is starting...", [ Name]),
                start_child(Sup, Server, Opts),
                ?PRINT_MSG("[done]~n")
        end, [
            {"ham server mysql",emysql_pool_manager,EmysqlOpts},
            {"ham server token check",token_check_interval},
            {"ham server package ets",package_ets}
        ]),
    {ok, Im} = application:get_env(ham_server,emqttd),
    {ok, _Pid} = apns:connect( my_apns, fun ?MODULE:handle_apns_error/2, fun ?MODULE:handle_apns_delete_subscription/1 ),
    DefaultConnection = apns:default_connection(),
    {ok, MaintainCertFile} = application:get_env( ham_server, maintain_cert_file ),
    {ok, MaintainPassword} = application:get_env( ham_server, maintain_password ),
    util:print("maintain cert file :~p maintain password: ~p ~n", [ MaintainCertFile, MaintainPassword ]),
    OwnerConnection = DefaultConnection#apns_connection{ cert_file = MaintainCertFile, cert_password = MaintainPassword },
    {ok, _PidOwner} = apns:connect( maintain_apns, OwnerConnection#apns_connection{error_fun = fun ?MODULE:handle_apns_error/2,
                                                   feedback_fun =  fun ?MODULE:handle_apns_delete_subscription/1}),
    {ok, CallBackUrl} = application:get_env(ham_server, call_back_url),
    lager:info("call_back_url:~p ~n", [CallBackUrl]),
    net_adm:ping(Im),
    tools:upload_url_create_pid(CallBackUrl),
    erlang:system_monitor(whereis(dets),[{large_heap,120000}]).

start_child(Sup, {supervisor, Name}) ->
    supervisor:start_child(Sup, supervisor_spec(Name));
start_child(Sup, Name) when is_atom(Name) ->
    {ok, _ChiId} = supervisor:start_child(Sup, worker_spec(Name)).

start_child(Sup, {supervisor, Name}, Opts) ->
    supervisor:start_child(Sup, supervisor_spec(Name, Opts));
start_child(Sup, Name, Opts) when is_atom(Name) ->
    {ok, _ChiId} = supervisor:start_child(Sup, worker_spec(Name, Opts)).

%%TODO: refactor...
supervisor_spec(Name) ->
  {Name,
    {Name, start_link, []},
    permanent, infinity, supervisor, [Name]}.

supervisor_spec(Name, Opts) ->
  {Name,
    {Name, start_link, [Opts]},
    permanent, infinity, supervisor, [Name]}.

worker_spec(Name) ->
  {Name,
    {Name, start_link, []},
    permanent, 5000, worker, [Name]}.
worker_spec(Name, Opts) ->
  {Name,
    {Name, start_link, [Opts]},
    permanent, 5000, worker, [Name]}.

print_banner() ->
	?PRINT("starting et-clould ham_server on node '~s'~n", [node()]).

print_vsn() ->
	{ok, Vsn} = application:get_key(vsn),
	{ok, Desc} = application:get_key(description),
	?PRINT("~s ~s is running now~n", [Desc, Vsn]).


    

%%------------------------------------------------------------------------------
%% @private
%% @doc
%% This function is called whenever an application has stopped. It
%% is intended to be the opposite of Module:start/2 and should do
%% any necessary cleaning up. The return value is ignored.
%%
%% @end
%%------------------------------------------------------------------------------
-spec stop(State :: term()) -> term().
stop(_State) ->
    ok.



